/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<asm.h>
#include	<map.h>
#include	<proc.h>
#include	<errno.h>
#include	<param.h>
#include	<string.h>

uint *kpgdir;
uint kend, kstk, phymem;
struct map kernmap[KMAPSIZ];
struct map coremap[CMAPSIZ];
struct map swapmap[SMAPSIZ];

void
minit(void)
{
        uint i;

        kstk = (uint)&end;
        tss = (uint *)&tss;
        kpgdir = (uint *)(kstk + PAGESIZ);
        phymem = *(uint *)MEM_CNT;
        kend = kstk + 2 * PAGESIZ;
	kpgdir[0] = 0x000000 | PG_P | PG_W | PG_PS;
	kpgdir[1] = 0x400000 | PG_P | PG_W | PG_PS;
	kpgdir[2] = 0x800000 | PG_P | PG_W | PG_PS;
	kpgdir[3] = 0xc00000 | PG_P | PG_W | PG_PS;
	for(i = PG_KERN_NUM; i < PG_TBL_NUM; i++)
		kpgdir[i] = 0;
	lcr3(kpgdir);
        memset(kernmap, 0, sizeof(kernmap));
        memset(coremap, 0, sizeof(coremap));
        memset(swapmap, 0, sizeof(swapmap));
	pagecnt = 0;
        for(i = kend; i < phymem; i += PAGESIZ){
                mfree(i < KERN_END ? kernmap : coremap, 1, i);
		if(i >= KERN_END)
			pagecnt++;
	}
	pagettl = pagecnt / 4;
}

void *
malloc0(struct map *mp, uint size)
{
	void *p;

	if(mp != kernmap){
		up->p_error = EINVAL;
		return NULL;
	}
	if(p = malloc(mp, size))
		memset(p, 0, PAGESIZ);
	return p;
}

void *
malloc(struct map *mp, uint size)
{
	uint a;
	struct map *bp;	
	
	for(bp = mp; bp->m_size; bp++){
		if(bp->m_size >= size){
			a = bp->m_addr;
			bp->m_addr += size;
		}
		if((bp->m_size -= size) == 0){
			do{
				bp++;
				(bp - 1)->m_addr = bp->m_addr;
			}while((bp - 1)->m_size = bp->m_size);
		}
		if(mp == coremap)
			pagecnt--;
		return (mp == swapmap ? (void *)(a * (PAGESIZ / SECTORSIZ)) : (void *)(a * PAGESIZ));
	}
	return NULL;
}

void
mfree(struct map *mp, uint size, uint aa)
{
	uint a, t;
	struct map *bp;

	a = (mp == swapmap ? (aa / (PAGESIZ / SECTORSIZ)) : (aa / PAGESIZ));
	for(bp = mp; bp->m_addr < a && bp->m_size; bp++)
		;
	if(bp > mp && (bp - 1)->m_addr + (bp - 1)->m_size == a){
		(bp - 1)->m_size += size;
		if(a + size == bp->m_addr){
			(bp - 1)->m_size += bp->m_size;
			while(bp->m_size){
				bp++;
				(bp - 1)->m_addr = bp->m_addr;
				(bp - 1)->m_size = bp->m_size;
			}
		}
	}else{
		if(a + size == bp->m_addr && bp->m_size){
			bp->m_addr -= size;
			bp->m_size += size;
		}else if(size){
			do{
				t = bp->m_addr;
				bp->m_addr = a;
				a = t;
				t = bp->m_size;
				bp->m_size = size;
				bp++;	
			}while(size = t);
		}
	}
	if(mp == coremap)
		pagecnt++;
}
